/**
 * &copy; 2006 NewInstance.it
 */
package it.newinstance.jrainbow.ui.web;

import it.newinstance.jrainbow.parser.Parser;
import it.newinstance.jrainbow.parser.UnsupportedParserException;
import it.newinstance.util.Configuration;
import it.newinstance.util.LabelValueBean;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.ServletContext;

/**
 * Dynamically finds CSS for supported languages
 * @author Luigi R. Viggiano
 * @version $Id: CSSLoader.java 138 2007-06-12 12:23:29Z luigi.viggiano $
 */
public class CSSLoader {
    private static Configuration conf = Configuration
            .getConfiguration(CSSLoader.class);
    private static final int CACHE_TIMEOUT_MILLIS = conf
            .getInt("cache.timeout.millis");
    private static final String LANGUAGES_KEY = CSSLoader.class.getName()
            + ".languages";
    private static final String STYLES_KEY = CSSLoader.class.getName()
            + ".styles";
    private static long LAST_LANGUAGE_LOAD_TIME;
    private static long LAST_STYLE_LOAD_TIME;
    
    private final String path;
    private final ServletContext context;

    /**
     * @param path path in which css for language style are located
     * @param servletContext the servlet context to load resources
     */
    public CSSLoader(String path, ServletContext servletContext) {
        this.path = path;
        this.context = servletContext;
    }

    @SuppressWarnings("unchecked")
    public Set<LabelValueBean> getLanguages() {
        Set<LabelValueBean> languages;
        if (CACHE_TIMEOUT_MILLIS > 0) {
            long now = System.currentTimeMillis();
            synchronized(context) {
                if ((now - LAST_LANGUAGE_LOAD_TIME) < CACHE_TIMEOUT_MILLIS) {
                    languages = (Set<LabelValueBean>) context
                            .getAttribute(LANGUAGES_KEY);
                    if (languages != null) {
                        log("Languages loaded from cache");
                        return languages;
                    } 
                }
            }
        }
        Set<String> resourcePaths = context.getResourcePaths(path);
        languages = new TreeSet<LabelValueBean>(getLabelComparator());
        for (String resourcePath : resourcePaths) {
            String value = resourcePath.split("/", 0)[3];
            try {
                if (value.startsWith(".")) {
                    log("Skipping hidden file " + resourcePath);
                    continue; 
                }
                
                Parser parser = Parser.Factory.newInstance(value);
                String label = parser.getLanguageName();
                languages.add(new LabelValueBean(label, value));
            } catch (UnsupportedParserException e) {
                log("Skipping unsupported language:" + value);
            }
        }
        if (CACHE_TIMEOUT_MILLIS > 0) {
            synchronized(context) {
                LAST_LANGUAGE_LOAD_TIME = System.currentTimeMillis();
                context.setAttribute(LANGUAGES_KEY, languages);
            }
        }
        return languages;
    }
    
    public String getJSONStyles(Set<LabelValueBean> languages) {
        String jsonStyles;
        if (CACHE_TIMEOUT_MILLIS > 0) {
            long now = System.currentTimeMillis();
            synchronized (context) {
                if ((now - LAST_STYLE_LOAD_TIME) < CACHE_TIMEOUT_MILLIS) {
                    jsonStyles = (String) context.getAttribute(STYLES_KEY);
                    if (jsonStyles != null) {
                        log("Styles loaded from cache");
                        return jsonStyles;
                    }
                }
            }
        }
        
        StringBuilder builder = new StringBuilder();
        builder.append("{");
        int i = 0;
        for (LabelValueBean language : languages) {
            if (i > 0) builder.append(",");
            builder.append("\"" + language.getValue() + "\":");
            builder.append("[");
            String languagePath = path + language.getValue();
            Set<LabelValueBean> styles = getStyles(languagePath);
            int j = 0;
            for (LabelValueBean style : styles) {
                if (j > 0) builder.append(",");
                builder.append("{");
                builder.append("\"label\":\"" + style.getLabel() + "\",");
                builder.append("\"value\":\"" + style.getValue() + "\"");
                builder.append("}");
                j++;
            }
            builder.append("]");
            i++;
        }
        builder.append("}");
        jsonStyles = builder.toString();
        if (CACHE_TIMEOUT_MILLIS > 0) {
            synchronized(context) {
                LAST_STYLE_LOAD_TIME = System.currentTimeMillis();
                context.setAttribute(STYLES_KEY, jsonStyles);
            }
        }
        return jsonStyles;
    }

    @SuppressWarnings("unchecked")
    private Set<LabelValueBean> getStyles(String languagePath) {
        Set<String> cssPaths = context.getResourcePaths(languagePath);
        Set<LabelValueBean> styles = new TreeSet<LabelValueBean>(
                getLabelComparator());
        for (String cssPath : cssPaths) {
            String value = cssPath.split("/", 0)[4];
            if (value.startsWith(".")) {
                log("Skipping hidden file " + cssPath);
                continue; 
            }
            styles.add(new LabelValueBean(getStyleLabel(cssPath), value));
        }
        return styles;        
    }

    private String getStyleLabel(String cssPath) {
        InputStream stream = context.getResourceAsStream(cssPath);
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(stream));
        String line;
        try {
            while ((line = reader.readLine()) != null) {
                if (line.trim().startsWith("Title:"))
                    return line.split(":", 0)[1].trim();
            }
            return null;
        } catch (IOException e) {
            log("I/O Exception reading file " + cssPath, e);
            return null;
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                log("I/O Exception closing file " + cssPath, e);
            }
        }
    }

    private Comparator<LabelValueBean> getLabelComparator() {
        return new Comparator<LabelValueBean>() {
            public int compare(LabelValueBean o1, LabelValueBean o2) {
                return o1.getLabel().compareTo(o2.getLabel());
            }
        };
    }
    
    private void log(String message) {
        log(message, null);
    }

    private void log(String message, Throwable e) {
        String logMessage = "CSSLoader: " + message;

        if (e == null)
            context.log(logMessage);
        else
            context.log(logMessage, e);
    }
}
